﻿using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Poem.Application.Dtos;
using Poem.Core.Entities;
using Poem.Core.Repositories;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Uow;

namespace Poem.Application.Services
{
    public class PoemAppService : ApplicationService, IPoemAppService
    {
        private readonly IRepository<Poet> _poetRepository;
        private readonly IPoemRepository _poemRepository;
        private readonly IRepository<Category> _categoryRepository;
        private readonly ICategoryPoemRepository _categoryPoemRepository;

        public PoemAppService(IRepository<Poet> poetRepository, IRepository<Category> categoryRepository,
            IPoemRepository poemRepository, ICategoryPoemRepository categoryPoemRepository)
        {
            _poetRepository = poetRepository;
            _poemRepository = poemRepository;
            _categoryPoemRepository = categoryPoemRepository;
            _categoryRepository = categoryRepository;
        }

        public async Task<CategoryDto> AddCategoryAsync(CategoryDto categoryDto)
        {
            var category = _categoryRepository.FirstOrDefault(o => o.CategoryName == categoryDto.CategoryName);
            if (category == null)
            {
                category = await _categoryRepository.InsertAsync(new Category { CategoryName = categoryDto.CategoryName }, true);
            }

            return ObjectMapper.Map<Category, CategoryDto>(category);
        }

        public async Task AddPoemToCategoryAsync(CategoryPoemDto categoryPoemDto)
        {
            var categoryPoem = _categoryPoemRepository.FirstOrDefault(o => o.CategoryId == categoryPoemDto.CategoryId && o.PoemId == categoryPoemDto.PoemId);
            if (categoryPoem == null)
            {
                categoryPoem = await _categoryPoemRepository.InsertAsync(new CategoryPoem { CategoryId = categoryPoemDto.CategoryId, PoemId = categoryPoemDto.PoemId }, true);
            }
        }

        public async Task<PoetDto> AddPoetAsync(PoetDto poetDto)
        {
            var poet = _poetRepository.FirstOrDefault(o => o.Name == poetDto.Name);
            if (poet == null)
            {
                poet = await _poetRepository.InsertAsync(new Poet { Name = poetDto.Name, Description = poetDto.Description }, true);
            }

            return ObjectMapper.Map<Poet, PoetDto>(poet);
        }

        public async Task DeleteCategory(CategoryDto categoryDto)
        {
            if (categoryDto.Id > 0)
            {
                var category = _categoryRepository.FirstOrDefault(o => o.Id == categoryDto.Id);
                if (category != null)
                {
                    await _categoryRepository.DeleteAsync(category, true);
                }
            }
            else if (!string.IsNullOrEmpty(categoryDto.CategoryName))
            {
                var category = _categoryRepository.FirstOrDefault(o => o.CategoryName == categoryDto.CategoryName);
                if (category != null)
                {
                    await _categoryRepository.DeleteAsync(category, true);
                }
            }
        }

        public List<CategoryDto> GetAllCategories()
        {
            return ObjectMapper.Map<List<Category>, List<CategoryDto>>(_categoryRepository.ToList());
        }

        public PagedResultDto<PoemDto> GetPagedPoems(PagedResultRequestDto dto)
        {
            var count = _poemRepository.Count();
            var list = _poemRepository.OrderBy(o => o.Id).PageBy(dto).ToList();
            return new PagedResultDto<PoemDto>
            {
                TotalCount = count,
                Items = ObjectMapper.Map<List<Core.Entities.Poem>, List<PoemDto>>(list)
            };
        }

        public PagedResultDto<PoetDto> GetPagedPoet(PagedResultRequestDto dto)
        {
            var count = _poetRepository.Count();
            var list = _poetRepository.OrderBy(o => o.Id).PageBy(dto).ToList();
            return new PagedResultDto<PoetDto>
            {
                TotalCount = count,
                Items = ObjectMapper.Map<List<Poet>, List<PoetDto>>(list)
            };
        }

        public List<CategoryDto> GetPoemCategories(int poemId)
        {
            var list = _categoryPoemRepository.GetPoemCategories(poemId);
            return ObjectMapper.Map<List<Category>, List<CategoryDto>>(list);
        }

        public List<PoemDto> GetPoemOfCategory(int categoryId)
        {
            var list = _categoryPoemRepository.GetPoemsOfCategory(categoryId);
            return ObjectMapper.Map<List<Core.Entities.Poem>, List<PoemDto>>(list);
        }

        public async Task RemovePoemFromCategory(CategoryPoemDto categoryPoemDto)
        {
            var categoryPoem = _categoryPoemRepository.FirstOrDefault(o => o.CategoryId == categoryPoemDto.CategoryId && o.PoemId == categoryPoemDto.PoemId);
            if (categoryPoem != null)
            {
                await _categoryPoemRepository.DeleteAsync(categoryPoem, true);
            }
        }

        public PagedResultDto<PoemDto> SearchPoems(SearchPoemDto dto)
        {
            int total;
            var list = _poemRepository.GetPagedPoems(dto.MaxResultCount, dto.SkipCount, dto.AuthorName, dto.Keyword, dto.Categories, out total).Result;
            return new PagedResultDto<PoemDto>
            {
                TotalCount = total,
                Items = ObjectMapper.Map<List<Core.Entities.Poem>, List<PoemDto>>(list)
            };
        }

        public PagedResultDto<PoetDto> SearchPoets(SearchPoetDto dto)
        {
            var res = _poetRepository.AsQueryable();
            if (!string.IsNullOrEmpty(dto.Keyword))
            {
                res = res.Where(o => o.Name.Contains(dto.Keyword));
            }
            var count = res.Count();
            var list = res.OrderBy(o => o.Id).PageBy(dto).ToList();

            return new PagedResultDto<PoetDto>
            {
                TotalCount = count,
                Items = ObjectMapper.Map<List<Poet>, List<PoetDto>>(list)
            };
        }

        public PagedResultDto<PoetDto> GetPagedPoets(PagedResultRequestDto dto)
        {
            using var uow = UnitOfWorkManager.Begin(new AbpUnitOfWorkOptions());
            var count = _poetRepository.Count();
            var list = _poetRepository.OrderBy(o => o.Id).PageBy(dto).ToList();

            return new PagedResultDto<PoetDto>
            {
                TotalCount = count,
                Items = ObjectMapper.Map<List<Poet>, List<PoetDto>>(list)
            };
        }
    }
}
